/**
 * Painter绘图模块
 * @module zrender/Painter
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *         errorrik (errorrik@gmail.com)
 *         pissang (https://www.github.com/pissang)
 */


'use strict';
var config = require('./config');
var util = require('./tool/util');
// var vec2 = require('./tool/vector');
var log = require('./tool/log');
// var matrix = require('./tool/matrix');
var BaseLoadingEffect = require('./loadingEffect/Base');
var Layer = require('./Layer');
// 返回false的方法，用于避免页面被选中
function returnFalse() {
    return false;
}
// 什么都不干的空方法
function doNothing() {
}
function isLayerValid(layer) {
    if (!layer) {
        return false;
    }
    if (layer.isBuildin) {
        return true;
    }
    if (typeof layer.resize !== 'function' || typeof layer.refresh !== 'function') {
        return false;
    }
    return true;
}
/**
         * @alias module:zrender/Painter
         * @constructor
         * @param {HTMLElement} root 绘图容器
         * @param {module:zrender/Storage} storage
         */
var Painter = function (root, storage) {
    /**
             * 绘图容器
             * @type {HTMLElement}
             */
    this.root = root;
    root.style['-webkit-tap-highlight-color'] = 'transparent';
    root.style['-webkit-user-select'] = 'none';
    root.style['user-select'] = 'none';
    root.style['-webkit-touch-callout'] = 'none';
    /**
             * @type {module:zrender/Storage}
             */
    this.storage = storage;
    root.innerHTML = '';
    this._width = this._getWidth();
    // 宽，缓存记录
    this._height = this._getHeight();
    // 高，缓存记录
    var domRoot = document.createElement('div');
    this._domRoot = domRoot;
    // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬
    domRoot.style.position = 'relative';
    domRoot.style.overflow = 'hidden';
    domRoot.style.width = this._width + 'px';
    domRoot.style.height = this._height + 'px';
    root.appendChild(domRoot);
    this._layers = {};
    this._zlevelList = [];
    this._layerConfig = {};
    this._loadingEffect = new BaseLoadingEffect({});
    this.shapeToImage = this._createShapeToImageProcessor();
    // 创建各层canvas
    // 背景
    this._bgDom = document.createElement('div');
    this._bgDom.style.cssText = [
        'position:absolute;left:0px;top:0px;width:',
        this._width,
        'px;height:',
        this._height + 'px;',
        '-webkit-user-select:none;user-select;none;',
        '-webkit-touch-callout:none;'
    ].join('');
    this._bgDom.setAttribute('data-zr-dom-id', 'bg');
    this._bgDom.className = config.elementClassName;
    domRoot.appendChild(this._bgDom);
    this._bgDom.onselectstart = returnFalse;
    // 高亮
    var hoverLayer = new Layer('_zrender_hover_', this);
    this._layers['hover'] = hoverLayer;
    domRoot.appendChild(hoverLayer.dom);
    hoverLayer.initContext();
    hoverLayer.dom.onselectstart = returnFalse;
    hoverLayer.dom.style['-webkit-user-select'] = 'none';
    hoverLayer.dom.style['user-select'] = 'none';
    hoverLayer.dom.style['-webkit-touch-callout'] = 'none';
    // Will be injected by zrender instance
    this.refreshNextFrame = null;
};
/**
         * 首次绘图，创建各种dom和context
         * 
         * @param {Function} callback 绘画结束后的回调函数
         */
Painter.prototype.render = function (callback) {
    if (this.isLoading()) {
        this.hideLoading();
    }
    // TODO
    this.refresh(callback, true);
    return this;
};
/**
         * 刷新
         * @param {Function} callback 刷新结束后的回调函数
         * @param {boolean} paintAll 强制绘制所有shape
         */
Painter.prototype.refresh = function (callback, paintAll) {
    var list = this.storage.getShapeList(true);
    this._paintList(list, paintAll);
    // Paint custum layers
    for (var i = 0; i < this._zlevelList.length; i++) {
        var z = this._zlevelList[i];
        var layer = this._layers[z];
        if (!layer.isBuildin && layer.refresh) {
            layer.refresh();
        }
    }
    if (typeof callback == 'function') {
        callback();
    }
    return this;
};
Painter.prototype._preProcessLayer = function (layer) {
    layer.unusedCount++;
    layer.updateTransform();
};
Painter.prototype._postProcessLayer = function (layer) {
    layer.dirty = false;
    // 删除过期的层
    // PENDING
    // if (layer.unusedCount >= 500) {
    //     this.delLayer(z);
    // }
    if (layer.unusedCount == 1) {
        layer.clear();
    }
};
Painter.prototype._paintList = function (list, paintAll) {
    if (typeof paintAll == 'undefined') {
        paintAll = false;
    }
    this._updateLayerStatus(list);
    var currentLayer;
    var currentZLevel;
    var ctx;
    this.eachBuildinLayer(this._preProcessLayer);
    // var invTransform = [];
    for (var i = 0, l = list.length; i < l; i++) {
        var shape = list[i];
        // Change draw layer
        if (currentZLevel !== shape.zlevel) {
            if (currentLayer) {
                if (currentLayer.needTransform) {
                    ctx.restore();
                }
                ctx.flush && ctx.flush();
            }
            currentZLevel = shape.zlevel;
            currentLayer = this.getLayer(currentZLevel);
            if (!currentLayer.isBuildin) {
                log('ZLevel ' + currentZLevel + ' has been used by unkown layer ' + currentLayer.id);
            }
            ctx = currentLayer.ctx;
            // Reset the count
            currentLayer.unusedCount = 0;
            if (currentLayer.dirty || paintAll) {
                currentLayer.clear();
            }
            if (currentLayer.needTransform) {
                ctx.save();
                currentLayer.setTransform(ctx);
            }
        }
        if ((currentLayer.dirty || paintAll) && !shape.invisible) {
            if (!shape.onbrush || shape.onbrush && !shape.onbrush(ctx, false)) {
                if (config.catchBrushException) {
                    try {
                        shape.brush(ctx, false, this.refreshNextFrame);
                    } catch (error) {
                        log(error, 'brush error of ' + shape.type, shape);
                    }
                } else {
                    shape.brush(ctx, false, this.refreshNextFrame);
                }
            }
        }
        shape.__dirty = false;
    }
    if (currentLayer) {
        if (currentLayer.needTransform) {
            ctx.restore();
        }
        ctx.flush && ctx.flush();
    }
    this.eachBuildinLayer(this._postProcessLayer);
};
/**
         * 获取 zlevel 所在层，如果不存在则会创建一个新的层
         * @param {number} zlevel
         * @return {module:zrender/Layer}
         */
Painter.prototype.getLayer = function (zlevel) {
    var layer = this._layers[zlevel];
    if (!layer) {
        // Create a new layer
        layer = new Layer(zlevel, this);
        layer.isBuildin = true;
        if (this._layerConfig[zlevel]) {
            util.merge(layer, this._layerConfig[zlevel], true);
        }
        layer.updateTransform();
        this.insertLayer(zlevel, layer);
        // Context is created after dom inserted to document
        // Or excanvas will get 0px clientWidth and clientHeight
        layer.initContext();
    }
    return layer;
};
Painter.prototype.insertLayer = function (zlevel, layer) {
    if (this._layers[zlevel]) {
        log('ZLevel ' + zlevel + ' has been used already');
        return;
    }
    // Check if is a valid layer
    if (!isLayerValid(layer)) {
        log('Layer of zlevel ' + zlevel + ' is not valid');
        return;
    }
    var len = this._zlevelList.length;
    var prevLayer = null;
    var i = -1;
    if (len > 0 && zlevel > this._zlevelList[0]) {
        for (i = 0; i < len - 1; i++) {
            if (this._zlevelList[i] < zlevel && this._zlevelList[i + 1] > zlevel) {
                break;
            }
        }
        prevLayer = this._layers[this._zlevelList[i]];
    }
    this._zlevelList.splice(i + 1, 0, zlevel);
    var prevDom = prevLayer ? prevLayer.dom : this._bgDom;
    if (prevDom.nextSibling) {
        prevDom.parentNode.insertBefore(layer.dom, prevDom.nextSibling);
    } else {
        prevDom.parentNode.appendChild(layer.dom);
    }
    this._layers[zlevel] = layer;
};
// Iterate each layer
Painter.prototype.eachLayer = function (cb, context) {
    for (var i = 0; i < this._zlevelList.length; i++) {
        var z = this._zlevelList[i];
        cb.call(context, this._layers[z], z);
    }
};
// Iterate each buildin layer
Painter.prototype.eachBuildinLayer = function (cb, context) {
    for (var i = 0; i < this._zlevelList.length; i++) {
        var z = this._zlevelList[i];
        var layer = this._layers[z];
        if (layer.isBuildin) {
            cb.call(context, layer, z);
        }
    }
};
// Iterate each other layer except buildin layer
Painter.prototype.eachOtherLayer = function (cb, context) {
    for (var i = 0; i < this._zlevelList.length; i++) {
        var z = this._zlevelList[i];
        var layer = this._layers[z];
        if (!layer.isBuildin) {
            cb.call(context, layer, z);
        }
    }
};
/**
         * 获取所有已创建的层
         * @param {Array.<module:zrender/Layer>} [prevLayer]
         */
Painter.prototype.getLayers = function () {
    return this._layers;
};
Painter.prototype._updateLayerStatus = function (list) {
    var layers = this._layers;
    var elCounts = {};
    this.eachBuildinLayer(function (layer, z) {
        elCounts[z] = layer.elCount;
        layer.elCount = 0;
    });
    for (var i = 0, l = list.length; i < l; i++) {
        var shape = list[i];
        var zlevel = shape.zlevel;
        var layer = layers[zlevel];
        if (layer) {
            layer.elCount++;
            // 已经被标记为需要刷新
            if (layer.dirty) {
                continue;
            }
            layer.dirty = shape.__dirty;
        }
    }
    // 层中的元素数量有发生变化
    this.eachBuildinLayer(function (layer, z) {
        if (elCounts[z] !== layer.elCount) {
            layer.dirty = true;
        }
    });
};
/**
         * 指定的图形列表
         * @param {Array.<module:zrender/shape/Base>} shapeList 需要更新的图形元素列表
         * @param {Function} [callback] 视图更新后回调函数
         */
Painter.prototype.refreshShapes = function (shapeList, callback) {
    for (var i = 0, l = shapeList.length; i < l; i++) {
        var shape = shapeList[i];
        shape.modSelf();
    }
    this.refresh(callback);
    return this;
};
/**
         * 设置loading特效
         * 
         * @param {Object} loadingEffect loading特效
         * @return {Painter}
         */
Painter.prototype.setLoadingEffect = function (loadingEffect) {
    this._loadingEffect = loadingEffect;
    return this;
};
/**
         * 清除hover层外所有内容
         */
Painter.prototype.clear = function () {
    this.eachBuildinLayer(this._clearLayer);
    return this;
};
Painter.prototype._clearLayer = function (layer) {
    layer.clear();
};
/**
         * 修改指定zlevel的绘制参数
         * 
         * @param {string} zlevel
         * @param {Object} config 配置对象
         * @param {string} [config.clearColor=0] 每次清空画布的颜色
         * @param {string} [config.motionBlur=false] 是否开启动态模糊
         * @param {number} [config.lastFrameAlpha=0.7]
         *                 在开启动态模糊的时候使用，与上一帧混合的alpha值，值越大尾迹越明显
         * @param {Array.<number>} [position] 层的平移
         * @param {Array.<number>} [rotation] 层的旋转
         * @param {Array.<number>} [scale] 层的缩放
         * @param {boolean} [zoomable=false] 层是否支持鼠标缩放操作
         * @param {boolean} [panable=false] 层是否支持鼠标平移操作
         */
Painter.prototype.modLayer = function (zlevel, config) {
    if (config) {
        if (!this._layerConfig[zlevel]) {
            this._layerConfig[zlevel] = config;
        } else {
            util.merge(this._layerConfig[zlevel], config, true);
        }
        var layer = this._layers[zlevel];
        if (layer) {
            util.merge(layer, this._layerConfig[zlevel], true);
        }
    }
};
/**
         * 删除指定层
         * @param {number} zlevel 层所在的zlevel
         */
Painter.prototype.delLayer = function (zlevel) {
    var layer = this._layers[zlevel];
    if (!layer) {
        return;
    }
    // Save config
    this.modLayer(zlevel, {
        position: layer.position,
        rotation: layer.rotation,
        scale: layer.scale
    });
    layer.dom.parentNode.removeChild(layer.dom);
    delete this._layers[zlevel];
    this._zlevelList.splice(util.indexOf(this._zlevelList, zlevel), 1);
};
/**
         * 刷新hover层
         */
Painter.prototype.refreshHover = function () {
    this.clearHover();
    var list = this.storage.getHoverShapes(true);
    for (var i = 0, l = list.length; i < l; i++) {
        this._brushHover(list[i]);
    }
    var ctx = this._layers.hover.ctx;
    ctx.flush && ctx.flush();
    this.storage.delHover();
    return this;
};
/**
         * 清除hover层所有内容
         */
Painter.prototype.clearHover = function () {
    var hover = this._layers.hover;
    hover && hover.clear();
    return this;
};
/**
         * 显示loading
         * 
         * @param {Object=} loadingEffect loading效果对象
         */
Painter.prototype.showLoading = function (loadingEffect) {
    this._loadingEffect && this._loadingEffect.stop();
    loadingEffect && this.setLoadingEffect(loadingEffect);
    this._loadingEffect.start(this);
    this.loading = true;
    return this;
};
/**
         * loading结束
         */
Painter.prototype.hideLoading = function () {
    this._loadingEffect.stop();
    this.clearHover();
    this.loading = false;
    return this;
};
/**
         * loading结束判断
         */
Painter.prototype.isLoading = function () {
    return this.loading;
};
/**
         * 区域大小变化后重绘
         */
Painter.prototype.resize = function () {
    var domRoot = this._domRoot;
    domRoot.style.display = 'none';
    var width = this._getWidth();
    var height = this._getHeight();
    domRoot.style.display = '';
    // 优化没有实际改变的resize
    if (this._width != width || height != this._height) {
        this._width = width;
        this._height = height;
        domRoot.style.width = width + 'px';
        domRoot.style.height = height + 'px';
        for (var id in this._layers) {
            this._layers[id].resize(width, height);
        }
        this.refresh(null, true);
    }
    return this;
};
/**
         * 清除单独的一个层
         * @param {number} zLevel
         */
Painter.prototype.clearLayer = function (zLevel) {
    var layer = this._layers[zLevel];
    if (layer) {
        layer.clear();
    }
};
/**
         * 释放
         */
Painter.prototype.dispose = function () {
    if (this.isLoading()) {
        this.hideLoading();
    }
    this.root.innerHTML = '';
    this.root = this.storage = this._domRoot = this._layers = null;
};
Painter.prototype.getDomHover = function () {
    return this._layers.hover.dom;
};
/**
         * 图像导出
         * @param {string} type
         * @param {string} [backgroundColor='#fff'] 背景色
         * @return {string} 图片的Base64 url
         */
Painter.prototype.toDataURL = function (type, backgroundColor, args) {
    if (window['G_vmlCanvasManager']) {
        return null;
    }
    var imageLayer = new Layer('image', this);
    this._bgDom.appendChild(imageLayer.dom);
    imageLayer.initContext();
    var ctx = imageLayer.ctx;
    imageLayer.clearColor = backgroundColor || '#fff';
    imageLayer.clear();
    var self = this;
    // 升序遍历，shape上的zlevel指定绘画图层的z轴层叠
    this.storage.iterShape(function (shape) {
        if (!shape.invisible) {
            if (!shape.onbrush || shape.onbrush && !shape.onbrush(ctx, false)) {
                if (config.catchBrushException) {
                    try {
                        shape.brush(ctx, false, self.refreshNextFrame);
                    } catch (error) {
                        log(error, 'brush error of ' + shape.type, shape);
                    }
                } else {
                    shape.brush(ctx, false, self.refreshNextFrame);
                }
            }
        }
    }, {
        normal: 'up',
        update: true
    });
    var image = imageLayer.dom.toDataURL(type, args);
    ctx = null;
    this._bgDom.removeChild(imageLayer.dom);
    return image;
};
/**
         * 获取绘图区域宽度
         */
Painter.prototype.getWidth = function () {
    return this._width;
};
/**
         * 获取绘图区域高度
         */
Painter.prototype.getHeight = function () {
    return this._height;
};
Painter.prototype._getWidth = function () {
    var root = this.root;
    var stl = root.currentStyle || document.defaultView.getComputedStyle(root);
    return ((root.clientWidth || parseInt(stl.width, 10)) - parseInt(stl.paddingLeft, 10)    // 请原谅我这比较粗暴
- parseInt(stl.paddingRight, 10)).toFixed(0) - 0;
};
Painter.prototype._getHeight = function () {
    var root = this.root;
    var stl = root.currentStyle || document.defaultView.getComputedStyle(root);
    return ((root.clientHeight || parseInt(stl.height, 10)) - parseInt(stl.paddingTop, 10)    // 请原谅我这比较粗暴
- parseInt(stl.paddingBottom, 10)).toFixed(0) - 0;
};
Painter.prototype._brushHover = function (shape) {
    var ctx = this._layers.hover.ctx;
    if (!shape.onbrush || shape.onbrush && !shape.onbrush(ctx, true)) {
        var layer = this.getLayer(shape.zlevel);
        if (layer.needTransform) {
            ctx.save();
            layer.setTransform(ctx);
        }
        // Retina 优化
        if (config.catchBrushException) {
            try {
                shape.brush(ctx, true, this.refreshNextFrame);
            } catch (error) {
                log(error, 'hoverBrush error of ' + shape.type, shape);
            }
        } else {
            shape.brush(ctx, true, this.refreshNextFrame);
        }
        if (layer.needTransform) {
            ctx.restore();
        }
    }
};
Painter.prototype._shapeToImage = function (id, shape, width, height, devicePixelRatio) {
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    canvas.style.width = width + 'px';
    canvas.style.height = height + 'px';
    canvas.setAttribute('width', width * devicePixelRatio);
    canvas.setAttribute('height', height * devicePixelRatio);
    ctx.clearRect(0, 0, width * devicePixelRatio, height * devicePixelRatio);
    var shapeTransform = {
        position: shape.position,
        rotation: shape.rotation,
        scale: shape.scale
    };
    shape.position = [
        0,
        0,
        0
    ];
    shape.rotation = 0;
    shape.scale = [
        1,
        1
    ];
    if (shape) {
        shape.brush(ctx, false);
    }
    var ImageShape = require('./shape/Image');
    var imgShape = new ImageShape({
        id: id,
        style: {
            x: 0,
            y: 0,
            image: canvas
        }
    });
    if (shapeTransform.position != null) {
        imgShape.position = shape.position = shapeTransform.position;
    }
    if (shapeTransform.rotation != null) {
        imgShape.rotation = shape.rotation = shapeTransform.rotation;
    }
    if (shapeTransform.scale != null) {
        imgShape.scale = shape.scale = shapeTransform.scale;
    }
    return imgShape;
};
Painter.prototype._createShapeToImageProcessor = function () {
    if (window['G_vmlCanvasManager']) {
        return doNothing;
    }
    var me = this;
    return function (id, e, width, height) {
        return me._shapeToImage(id, e, width, height, config.devicePixelRatio);
    };
};
module.exports = Painter || module.exports;;